<!doctype html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>njet stream traffic status monitor</title>
        <style>
            body {
                background:  white;
                color:       black;
                font-family: Helvetica, Arial, sans-serif;
            }

            h1 {
                margin: .5em 0 0 0;
            }

            h2 {
                margin: .8em 0 .3em 0;
            }

            h3 {
                margin: .5em 0 .3em 0;
            }

            table {
                font-size: .8em;
                margin: .5em 0;
                border-collapse: collapse;
                border-bottom: 1px #DED solid;
            }

            thead th {
                font-size: 1em;
                background: #DED;
                padding: .1em .3em;
                border: .2em solid #FFF;
            }

            tbody tr.odd {
                background: #F5F5F5;
            }

            tbody th {
                text-align: left;
            }

            tbody td {
                height: 1.2em;
                text-align: right;
            }

            tbody td.key {
                font-size: 1em;
                background: #DED;
                padding: .1em .3em;
                border: .2em solid #FFF;
            }

            div.update {
                margin-top: 32px;
                color: #696969;
            }

            div.footer {
                margin-top: 16px;
            }

            a:link {
                color: #6666ff;
                font-size: 0.7em;
            }

            a:visited {
                color: #6666ff;
                font-size: 0.7em;
            }

            a:active {
                color: #222222;
                font-size: 0.7em;
            }

            a:hover {
                color: #000000;
                background-color: #e0e8ff;
                font-size: 0.7em;
            }

            /* from https://www.flag-sprites.com */
            .flag {
                width: 16px;
                height: 11px;
                background: url("") no-repeat;
            }

            .flag.flag-ad {background-position: -16px 0}
            .flag.flag-ae {background-position: -32px 0}
            .flag.flag-af {background-position: -48px 0}
            .flag.flag-ag {background-position: -64px 0}
            .flag.flag-ai {background-position: -80px 0}
            .flag.flag-al {background-position: -96px 0}
            .flag.flag-am {background-position: -112px 0}
            .flag.flag-an {background-position: -128px 0}
            .flag.flag-ao {background-position: -144px 0}
            .flag.flag-ar {background-position: -160px 0}
            .flag.flag-as {background-position: -176px 0}
            .flag.flag-at {background-position: -192px 0}
            .flag.flag-au {background-position: -208px 0}
            .flag.flag-aw {background-position: -224px 0}
            .flag.flag-az {background-position: -240px 0}
            .flag.flag-ba {background-position: 0 -11px}
            .flag.flag-bb {background-position: -16px -11px}
            .flag.flag-bd {background-position: -32px -11px}
            .flag.flag-be {background-position: -48px -11px}
            .flag.flag-bf {background-position: -64px -11px}
            .flag.flag-bg {background-position: -80px -11px}
            .flag.flag-bh {background-position: -96px -11px}
            .flag.flag-bi {background-position: -112px -11px}
            .flag.flag-bj {background-position: -128px -11px}
            .flag.flag-bm {background-position: -144px -11px}
            .flag.flag-bn {background-position: -160px -11px}
            .flag.flag-bo {background-position: -176px -11px}
            .flag.flag-br {background-position: -192px -11px}
            .flag.flag-bs {background-position: -208px -11px}
            .flag.flag-bt {background-position: -224px -11px}
            .flag.flag-bv {background-position: -240px -11px}
            .flag.flag-bw {background-position: 0 -22px}
            .flag.flag-by {background-position: -16px -22px}
            .flag.flag-bz {background-position: -32px -22px}
            .flag.flag-ca {background-position: -48px -22px}
            .flag.flag-catalonia {background-position: -64px -22px}
            .flag.flag-cd {background-position: -80px -22px}
            .flag.flag-cf {background-position: -96px -22px}
            .flag.flag-cg {background-position: -112px -22px}
            .flag.flag-ch {background-position: -128px -22px}
            .flag.flag-ci {background-position: -144px -22px}
            .flag.flag-ck {background-position: -160px -22px}
            .flag.flag-cl {background-position: -176px -22px}
            .flag.flag-cm {background-position: -192px -22px}
            .flag.flag-cn {background-position: -208px -22px}
            .flag.flag-co {background-position: -224px -22px}
            .flag.flag-cr {background-position: -240px -22px}
            .flag.flag-cu {background-position: 0 -33px}
            .flag.flag-cv {background-position: -16px -33px}
            .flag.flag-cw {background-position: -32px -33px}
            .flag.flag-cy {background-position: -48px -33px}
            .flag.flag-cz {background-position: -64px -33px}
            .flag.flag-de {background-position: -80px -33px}
            .flag.flag-dj {background-position: -96px -33px}
            .flag.flag-dk {background-position: -112px -33px}
            .flag.flag-dm {background-position: -128px -33px}
            .flag.flag-do {background-position: -144px -33px}
            .flag.flag-dz {background-position: -160px -33px}
            .flag.flag-ec {background-position: -176px -33px}
            .flag.flag-ee {background-position: -192px -33px}
            .flag.flag-eg {background-position: -208px -33px}
            .flag.flag-eh {background-position: -224px -33px}
            .flag.flag-england {background-position: -240px -33px}
            .flag.flag-er {background-position: 0 -44px}
            .flag.flag-es {background-position: -16px -44px}
            .flag.flag-et {background-position: -32px -44px}
            .flag.flag-eu {background-position: -48px -44px}
            .flag.flag-fi {background-position: -64px -44px}
            .flag.flag-fj {background-position: -80px -44px}
            .flag.flag-fk {background-position: -96px -44px}
            .flag.flag-fm {background-position: -112px -44px}
            .flag.flag-fo {background-position: -128px -44px}
            .flag.flag-fr {background-position: -144px -44px}
            .flag.flag-ga {background-position: -160px -44px}
            .flag.flag-gb {background-position: -176px -44px}
            .flag.flag-gd {background-position: -192px -44px}
            .flag.flag-ge {background-position: -208px -44px}
            .flag.flag-gf {background-position: -224px -44px}
            .flag.flag-gg {background-position: -240px -44px}
            .flag.flag-gh {background-position: 0 -55px}
            .flag.flag-gi {background-position: -16px -55px}
            .flag.flag-gl {background-position: -32px -55px}
            .flag.flag-gm {background-position: -48px -55px}
            .flag.flag-gn {background-position: -64px -55px}
            .flag.flag-gp {background-position: -80px -55px}
            .flag.flag-gq {background-position: -96px -55px}
            .flag.flag-gr {background-position: -112px -55px}
            .flag.flag-gs {background-position: -128px -55px}
            .flag.flag-gt {background-position: -144px -55px}
            .flag.flag-gu {background-position: -160px -55px}
            .flag.flag-gw {background-position: -176px -55px}
            .flag.flag-gy {background-position: -192px -55px}
            .flag.flag-hk {background-position: -208px -55px}
            .flag.flag-hm {background-position: -224px -55px}
            .flag.flag-hn {background-position: -240px -55px}
            .flag.flag-hr {background-position: 0 -66px}
            .flag.flag-ht {background-position: -16px -66px}
            .flag.flag-hu {background-position: -32px -66px}
            .flag.flag-ic {background-position: -48px -66px}
            .flag.flag-id {background-position: -64px -66px}
            .flag.flag-ie {background-position: -80px -66px}
            .flag.flag-il {background-position: -96px -66px}
            .flag.flag-im {background-position: -112px -66px}
            .flag.flag-in {background-position: -128px -66px}
            .flag.flag-io {background-position: -144px -66px}
            .flag.flag-iq {background-position: -160px -66px}
            .flag.flag-ir {background-position: -176px -66px}
            .flag.flag-is {background-position: -192px -66px}
            .flag.flag-it {background-position: -208px -66px}
            .flag.flag-je {background-position: -224px -66px}
            .flag.flag-jm {background-position: -240px -66px}
            .flag.flag-jo {background-position: 0 -77px}
            .flag.flag-jp {background-position: -16px -77px}
            .flag.flag-ke {background-position: -32px -77px}
            .flag.flag-kg {background-position: -48px -77px}
            .flag.flag-kh {background-position: -64px -77px}
            .flag.flag-ki {background-position: -80px -77px}
            .flag.flag-km {background-position: -96px -77px}
            .flag.flag-kn {background-position: -112px -77px}
            .flag.flag-kp {background-position: -128px -77px}
            .flag.flag-kr {background-position: -144px -77px}
            .flag.flag-kurdistan {background-position: -160px -77px}
            .flag.flag-kw {background-position: -176px -77px}
            .flag.flag-ky {background-position: -192px -77px}
            .flag.flag-kz {background-position: -208px -77px}
            .flag.flag-la {background-position: -224px -77px}
            .flag.flag-lb {background-position: -240px -77px}
            .flag.flag-lc {background-position: 0 -88px}
            .flag.flag-li {background-position: -16px -88px}
            .flag.flag-lk {background-position: -32px -88px}
            .flag.flag-lr {background-position: -48px -88px}
            .flag.flag-ls {background-position: -64px -88px}
            .flag.flag-lt {background-position: -80px -88px}
            .flag.flag-lu {background-position: -96px -88px}
            .flag.flag-lv {background-position: -112px -88px}
            .flag.flag-ly {background-position: -128px -88px}
            .flag.flag-ma {background-position: -144px -88px}
            .flag.flag-mc {background-position: -160px -88px}
            .flag.flag-md {background-position: -176px -88px}
            .flag.flag-me {background-position: -192px -88px}
            .flag.flag-mg {background-position: -208px -88px}
            .flag.flag-mh {background-position: -224px -88px}
            .flag.flag-mk {background-position: -240px -88px}
            .flag.flag-ml {background-position: 0 -99px}
            .flag.flag-mm {background-position: -16px -99px}
            .flag.flag-mn {background-position: -32px -99px}
            .flag.flag-mo {background-position: -48px -99px}
            .flag.flag-mp {background-position: -64px -99px}
            .flag.flag-mq {background-position: -80px -99px}
            .flag.flag-mr {background-position: -96px -99px}
            .flag.flag-ms {background-position: -112px -99px}
            .flag.flag-mt {background-position: -128px -99px}
            .flag.flag-mu {background-position: -144px -99px}
            .flag.flag-mv {background-position: -160px -99px}
            .flag.flag-mw {background-position: -176px -99px}
            .flag.flag-mx {background-position: -192px -99px}
            .flag.flag-my {background-position: -208px -99px}
            .flag.flag-mz {background-position: -224px -99px}
            .flag.flag-na {background-position: -240px -99px}
            .flag.flag-nc {background-position: 0 -110px}
            .flag.flag-ne {background-position: -16px -110px}
            .flag.flag-nf {background-position: -32px -110px}
            .flag.flag-ng {background-position: -48px -110px}
            .flag.flag-ni {background-position: -64px -110px}
            .flag.flag-nl {background-position: -80px -110px}
            .flag.flag-no {background-position: -96px -110px}
            .flag.flag-np {background-position: -112px -110px}
            .flag.flag-nr {background-position: -128px -110px}
            .flag.flag-nu {background-position: -144px -110px}
            .flag.flag-nz {background-position: -160px -110px}
            .flag.flag-om {background-position: -176px -110px}
            .flag.flag-pa {background-position: -192px -110px}
            .flag.flag-pe {background-position: -208px -110px}
            .flag.flag-pf {background-position: -224px -110px}
            .flag.flag-pg {background-position: -240px -110px}
            .flag.flag-ph {background-position: 0 -121px}
            .flag.flag-pk {background-position: -16px -121px}
            .flag.flag-pl {background-position: -32px -121px}
            .flag.flag-pm {background-position: -48px -121px}
            .flag.flag-pn {background-position: -64px -121px}
            .flag.flag-pr {background-position: -80px -121px}
            .flag.flag-ps {background-position: -96px -121px}
            .flag.flag-pt {background-position: -112px -121px}
            .flag.flag-pw {background-position: -128px -121px}
            .flag.flag-py {background-position: -144px -121px}
            .flag.flag-qa {background-position: -160px -121px}
            .flag.flag-re {background-position: -176px -121px}
            .flag.flag-ro {background-position: -192px -121px}
            .flag.flag-rs {background-position: -208px -121px}
            .flag.flag-ru {background-position: -224px -121px}
            .flag.flag-rw {background-position: -240px -121px}
            .flag.flag-sa {background-position: 0 -132px}
            .flag.flag-sb {background-position: -16px -132px}
            .flag.flag-sc {background-position: -32px -132px}
            .flag.flag-scotland {background-position: -48px -132px}
            .flag.flag-sd {background-position: -64px -132px}
            .flag.flag-se {background-position: -80px -132px}
            .flag.flag-sg {background-position: -96px -132px}
            .flag.flag-sh {background-position: -112px -132px}
            .flag.flag-si {background-position: -128px -132px}
            .flag.flag-sk {background-position: -144px -132px}
            .flag.flag-sl {background-position: -160px -132px}
            .flag.flag-sm {background-position: -176px -132px}
            .flag.flag-sn {background-position: -192px -132px}
            .flag.flag-so {background-position: -208px -132px}
            .flag.flag-somaliland {background-position: -224px -132px}
            .flag.flag-sr {background-position: -240px -132px}
            .flag.flag-ss {background-position: 0 -143px}
            .flag.flag-st {background-position: -16px -143px}
            .flag.flag-sv {background-position: -32px -143px}
            .flag.flag-sx {background-position: -48px -143px}
            .flag.flag-sy {background-position: -64px -143px}
            .flag.flag-sz {background-position: -80px -143px}
            .flag.flag-tc {background-position: -96px -143px}
            .flag.flag-td {background-position: -112px -143px}
            .flag.flag-tf {background-position: -128px -143px}
            .flag.flag-tg {background-position: -144px -143px}
            .flag.flag-th {background-position: -160px -143px}
            .flag.flag-tibet {background-position: -176px -143px}
            .flag.flag-tj {background-position: -192px -143px}
            .flag.flag-tk {background-position: -208px -143px}
            .flag.flag-tl {background-position: -224px -143px}
            .flag.flag-tm {background-position: -240px -143px}
            .flag.flag-tn {background-position: 0 -154px}
            .flag.flag-to {background-position: -16px -154px}
            .flag.flag-tr {background-position: -32px -154px}
            .flag.flag-tt {background-position: -48px -154px}
            .flag.flag-tv {background-position: -64px -154px}
            .flag.flag-tw {background-position: -80px -154px}
            .flag.flag-tz {background-position: -96px -154px}
            .flag.flag-ua {background-position: -112px -154px}
            .flag.flag-ug {background-position: -128px -154px}
            .flag.flag-um {background-position: -144px -154px}
            .flag.flag-us {background-position: -160px -154px}
            .flag.flag-uy {background-position: -176px -154px}
            .flag.flag-uz {background-position: -192px -154px}
            .flag.flag-va {background-position: -208px -154px}
            .flag.flag-vc {background-position: -224px -154px}
            .flag.flag-ve {background-position: -240px -154px}
            .flag.flag-vg {background-position: 0 -165px}
            .flag.flag-vi {background-position: -16px -165px}
            .flag.flag-vn {background-position: -32px -165px}
            .flag.flag-vu {background-position: -48px -165px}
            .flag.flag-wales {background-position: -64px -165px}
            .flag.flag-wf {background-position: -80px -165px}
            .flag.flag-ws {background-position: -96px -165px}
            .flag.flag-xk {background-position: -112px -165px}
            .flag.flag-ye {background-position: -128px -165px}
            .flag.flag-yt {background-position: -144px -165px}
            .flag.flag-za {background-position: -160px -165px}
            .flag.flag-zanzibar {background-position: -176px -165px}
            .flag.flag-zm {background-position: -192px -165px}
            .flag.flag-zw {background-position: -208px -165px}
        </style>
    </head>

    <body>

        <h1>
            NJet Stream Traffic Status
        </h1>

        <div id="monitor"></div>

        <div id="update" class="update">
            <strong>update interval:</strong>
            <select id="refresh" onchange="vtsSetInterval(this.value * 1000)">
                <option value="1">1</option>
                <option value="2">2</option>
                <option value="3">3</option>
                <option value="4">4</option>
                <option value="5">5</option>
                <option value="6">6</option>
                <option value="7">7</option>
                <option value="8">8</option>
            </select>
            <strong>sec</strong>
        </div>

        <div id="footer" class="footer">
            <a href="{{uri}}/format/json" id="jsonUri"><strong>JSON</strong></a>
            | <a href="https://github.com/vozlt/nginx-module-sts"><strong>GITHUB</strong></a>
        </div>

        <script>
            String.prototype.format = function () {
                var i = 0, args = arguments;
                return this.replace(/{}/g, function () {
                    return typeof args[i] != 'undefined' ? args[i++] : '';
                });
            };

            var vtsStatusURI = "{{uri}}/format/json", vtsUpdateInterval = 1000, vtsUpdate;

            var vtsStatusVars = {
                titles: {
                    main:"Server main",
                    server:"Server zones",
                    filter:"Filters",
                    upstream:"Upstreams"
                },
                ids: {
                    server:"streamServerZones",
                    filter:"streamFilterZones",
                    upstream:"streamUpstreamZones"
                }
            };

            var aPs = (function() {
                var data = [];
                var msec = {
                    last: undefined,
                    period: undefined
                };

                return {
                    getValue: function(key, value) {
                        if (typeof data[key] === 'undefined') {
                            data[key] = value;
                            return 'n/a';
                        } else {
                            var increase = value - data[key];
                            data[key] = value;
                            return Math.floor(increase * 1000 / msec.period);
                        }
                    },

                    refresh: function(time) {
                        msec.period = time - msec.last;
                        msec.last = time;
                    }
                };

            })();

            function mTh(msec) {
                var ms = 1000, m = 60, h = m * m, d = h * 24, s = '';

                if (msec < ms) {
                return msec + 'ms';
                }

                if (msec < (ms * m)) {
                return Math.floor(msec / ms) + '.' + Math.floor((msec % ms) / 10) + 's';
                }

                var days = Math.floor(msec / (d * ms));
                if (days) {
                s += days + "d ";
                }

                var hours = Math.floor((msec % (d * ms)) / (h * ms));
                if (days || hours) {
                s += hours + "h ";
                }

                var minutes = Math.floor(((msec % (d * ms)) % (h * ms)) / (m * ms));
                if (days || hours || minutes) {
                s += minutes + "m ";
                }

                var seconds = Math.floor((((msec % (d * ms)) % (h * ms)) % (m * ms)) / ms);

                return s + seconds + "s";
            }

            function bTh(b) {
                var kb = 1024;
                if (typeof b !== 'number') {
                    return b;
                }

                if (b < kb) {
                    return b + ' B';
                }

                if (b < (kb * kb)) {
                    return (b / kb).toFixed(1) + ' KiB';
                }

                if (b < (kb * kb * kb)) {
                    return (b / (kb * kb)).toFixed(1) + ' MiB';
                }

                if (b < (kb * kb * kb * kb)) {
                    return (b / (kb * kb * kb)).toFixed(2) + ' GiB';
                }

                return (b / (kb * kb * kb * kb)).toFixed(6) + ' TiB';
            }

            function sTh(b,d) {
                var s;
                if (!b && !d) {
                    s = "up";
                } else if (d) {
                    s = "down";
                } else {
                    s = "backup";
                }
                return s;
            }

            function sHe(t, v) {
                var ts = t.split(' ');
                return '<' + t + '>' + (v) + '</' + ts[0] + '>';
            }

            function aHe(t, v) {
                var i;
                var o = '';
                if (Object.prototype.toString.call(v) === '[object Array]') {
                    for (i = 0; i < v.length; i++)  {
                        o += sHe(t, v[i]);
                    }
                } else {
                    o = sHe(t, v);
                }
                return o;
            }

            function templateServerHeader() {
                var heads = [];

                heads[0] = '{}{}{}{}{}{}'.format(aHe('th rowspan="2"', 'Zone'),
                                             aHe('th rowspan="2"', 'Port'),
                                             aHe('th rowspan="2"', 'Protocol'),
                                             aHe('th colspan="3"', 'Requests'),
                                             aHe('th colspan="6"', 'Responses'),
                                             aHe('th colspan="4"', 'Traffic'));


                heads[1] = aHe('th', ['Total', 'Req/s', 'Time', '1xx', '2xx', '3xx', '4xx', '5xx',
                                      'Total', 'Sent', 'Rcvd', 'Sent/s', 'Rcvd/s']);

                return aHe('thead', '{}{}'.format(aHe('tr', heads[0]), aHe('tr', heads[1])));
            }

            function templateUpstreamHeader() {
                var heads = [];

                heads[0] = '{}{}{}{}{}{}{}{}{}'.format(aHe('th rowspan="2"', 'Server'),
                                                       aHe('th rowspan="2"', 'State'),
                                                       aHe('th colspan="3"', 'Response Time'),
                                                       aHe('th rowspan="2"', 'Weight'),
                                                       aHe('th rowspan="2"', 'MaxFails'),
                                                       aHe('th rowspan="2"', 'FailTimeout'),
                                                       aHe('th colspan="3"', 'Requests'),
                                                       aHe('th colspan="6"', 'Responses'),
                                                       aHe('th colspan="4"', 'Traffic'));

                heads[1] = aHe('th', ['Connect', 'FirstByte', 'Response', 'Total', 'Req/s',
                                      'Time', '1xx', '2xx', '3xx', '4xx', '5xx',
                                      'Total', 'Sent', 'Rcvd', 'Sent/s', 'Rcvd/s']);

                return aHe('thead', '{}{}'.format(aHe('tr', heads[0]), aHe('tr', heads[1])));
            }

            function templateMainZone(it) {
                var o = '';
                var head = '';
                var body = '';
                var heads = [];
                var bodys = [];

                heads[0] = '{}{}{}{}{}{}'.format(aHe('th rowspan="2"', 'Host'),
                                               aHe('th rowspan="2"', 'Version'),
                                               aHe('th rowspan="2"', 'Uptime'),
                                               aHe('th colspan="4"', 'Connections'),
                                               aHe('th colspan="4"', 'Requests'),
                                               aHe('th colspan="4"', 'Shared memory'));

                heads[1] = aHe('th', ['active', 'reading', 'writing', 'waiting', 'accepted',
                                      'handled', 'Total', 'Req/s', 'name', 'maxSize', 'usedSize', 'usedNode']);

                head = aHe('thead', '{}{}'.format(aHe('tr', heads[0]), aHe('tr', heads[1])));

                bodys[0] = aHe('td', [ aHe('strong', it.hostName), it.njetVersion, mTh(it.nowMsec - it.loadMsec),
                                      it.connections.active, it.connections.reading, it.connections.writing,
                                      it.connections.waiting, it.connections.accepted, it.connections.handled,
                                      it.connections.requests, aPs.getValue('main.connections.requests', it.connections.requests),
                                      aHe('strong', it.sharedZones.name), bTh(it.sharedZones.maxSize), bTh(it.sharedZones.usedSize),
                                      it.sharedZones.usedNode]);

                body = aHe('tbody', aHe('tr', bodys[0]));

                o = '{}{}'.format(aHe('h2', vtsStatusVars.titles.main), aHe('table', '{}{}'.format(head, body)));
                o = aHe('div id="{}"'.format(vtsStatusVars.ids.main), o);

                return o;
            }

            function templateServerZone(filter, group, id) {
                var n = 0;
                var s = '';
                var o = '';

                for(var name in filter) {
                    var zone = filter[name];
                    var uniq = '{}.{}.{}'.format(id, group, name);
                    var clas = '';
                    var flag = '';
                    var responseCount = 0;
                    var responseTotal = 0;

                    clas = (n++ % 2) ? 'odd' : '';
                    flag = (group.indexOf("country") != -1 && name.length == 2)
                           ? '<img class="flag flag-{}" />'.format(name.toLowerCase())
                           : '';

                    s = '{}{}'.format(aHe('th', '{}{}'.format(flag, name)),
                                      aHe('td', [zone.port, zone.protocol,
                                                 (zone.connectCounter + zone.overCounts['maxIntegerSize'] * zone.overCounts['connectCounter']),
                                                 (aPs.getValue(uniq + '.connectCounter', zone.connectCounter)),
                                                 mTh(zone.sessionMsec)
                                                ]));

                    for(var code in zone.responses) {
                        responseCount = zone.responses[code] + zone.overCounts['maxIntegerSize'] * zone.overCounts[code];
                        responseTotal += responseCount;
                        s = '{}{}'.format(s, aHe('td', responseCount));
                    }

                    s = '{}{}'.format(s, aHe('td', [responseTotal,
                                                    bTh(zone.outBytes + zone.overCounts['maxIntegerSize'] * zone.overCounts['outBytes']),
                                                    bTh(zone.inBytes + zone.overCounts['maxIntegerSize'] * zone.overCounts['inBytes']),
                                                    bTh(aPs.getValue('{}.{}'.format(uniq, 'outBytes'), zone.outBytes)),
                                                    bTh(aPs.getValue('{}.{}'.format(uniq, 'inBytes'), zone.inBytes))
                                                   ]));


                    o = '{}{}'.format(o, aHe('tr class="{}"'.format(clas), s));
                }

                return o;
            }

            function templateUpstreamZone(filter, group, id) {
                var n = 0;
                var s = '';
                var o = '';

                while (n < filter.length) {
                    var peer = filter[n];
                    var uniq = '{}.{}.{}'.format(id, group, peer.server);
                    var clas = '';
                    var responseCount = 0;
                    var responseTotal = 0;

                    clas = (n++ % 2) ? 'odd' : '';

                    s = '{}{}'.format(aHe('th', peer.server),
                                      aHe('td', [sTh(peer.backup, peer.down), mTh(peer.uConnectMsec),
                                                 mTh(peer.uFirstByteMsec), mTh(peer.uSessionMsec),
                                                 peer.weight, peer.maxFails, peer.failTimeout,
                                                 (peer.connectCounter + peer.overCounts['maxIntegerSize'] * peer.overCounts['connectCounter']),
                                                 aPs.getValue(uniq + '.connectCounter', peer.connectCounter),
                                                 mTh(peer.sessionMsec)
                                                ]));

                    for(var code in peer.responses) {
                        responseCount = peer.responses[code] + peer.overCounts['maxIntegerSize'] * peer.overCounts[code];
                        responseTotal += responseCount;
                        s = '{}{}'.format(s, aHe('td', responseCount));
                    }

                    s = '{}{}'.format(s, aHe('td', [responseTotal,
                                                    bTh(peer.outBytes + peer.overCounts['maxIntegerSize'] * peer.overCounts['outBytes']),
                                                    bTh(peer.inBytes + peer.overCounts['maxIntegerSize'] * peer.overCounts['inBytes']),
                                                    bTh(aPs.getValue('{}.{}'.format(uniq, 'outBytes'), peer.outBytes)),
                                                    bTh(aPs.getValue('{}.{}'.format(uniq, 'inBytes'), peer.inBytes))
                                                   ]));

                    o = '{}{}'.format(o, aHe('tr class="{}"'.format(clas), s));
                }

                return o;
            }

            function template(it) {
                aPs.refresh(it.nowMsec);

                var out = '';
                var tmp = '';
                var head = '';
                var body = '';
                var bodys = [];
                var cache = true;

                /* main */
                out = templateMainZone(it);

                /* serverZones */
                head = templateServerHeader();
                bodys[0] = templateServerZone(it.streamServerZones, 'server', vtsStatusVars.ids.server);
                body = aHe('tbody', bodys[0]);
                out = '{}{}'.format(out, aHe('div id="{}"'.format(vtsStatusVars.ids.server),
                                             '{}{}'.format(aHe('h2', vtsStatusVars.titles.server),
                                                           aHe('table', '{}{}'.format(head, body)))));

                /* filterZones */
                if (vtsStatusVars.ids.filter in it) {
                    tmp = '';
                    for (var group in it.streamFilterZones) {
                        var filter = it.streamFilterZones[group];
                        head = templateServerHeader(cache);
                        bodys[0] = templateServerZone(filter, group, vtsStatusVars.ids.filter, cache);
                        body = aHe('tbody', bodys[0]);
                        tmp = '{}{}{}'.format(tmp, aHe('h3', group), aHe('table', '{}{}'.format(head, body)));
                    }
                    out = '{}{}'.format(out, aHe('div id="{}"'.format(vtsStatusVars.ids.filter),
                                                 '{}{}'.format(aHe('h2', vtsStatusVars.titles.filter), tmp)));
                }

                /* upstreamZones */
                if (vtsStatusVars.ids.upstream in it) {
                    tmp = '';
                    for (var group in it.streamUpstreamZones) {
                        var filter = it.streamUpstreamZones[group];
                        head = templateUpstreamHeader();
                        bodys[0] = templateUpstreamZone(filter, group, vtsStatusVars.ids.upstream);
                        body = aHe('tbody', bodys[0]);
                        tmp = '{}{}{}'.format(tmp, aHe('h3', group), aHe('table', '{}{}'.format(head, body)));
                    }
                    out = '{}{}'.format(out, aHe('div id="{}"'.format(vtsStatusVars.ids.upstream),
                                                 '{}{}'.format(aHe('h2', vtsStatusVars.titles.upstream), tmp)));
                }

                return out;
            }

            function vtsGetData() {
                var monitor = document.getElementById('monitor');
                var request = new XMLHttpRequest;
                request.onload = function() {
                    if (this.status != 200) {
                        return;
                    }
                    monitor.innerHTML = template(JSON.parse(this.responseText));
                }
                request.open('GET', vtsStatusURI);
                request.send();
            };

            function vtsSetInterval(msec) {
                clearInterval(vtsUpdate);
                vtsUpdate = setInterval(vtsGetData, msec);
            }

            document.getElementById('jsonUri').href = vtsStatusURI;

            vtsGetData();

            vtsSetInterval(vtsUpdateInterval);
        </script>
    </body>
</html>
